﻿
namespace Patterns4Net.Tests.PatternEnforcer.Core.Patterns
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics.Contracts;
    using Mono.Cecil;
    using NUnit.Framework;
    using Patterns4Net.Attributes;
    using Patterns4Net.Core.Patterns;
    using Patterns4Net.PatternEnforcer.Core.Patterns;

    [TestFixture]
    public class MEFPatternCheckersLocatorTest
    {
        private static readonly Lazy<IPatternChecker, PatternCheckerExportAttribute>[]
            emptycheckersArray = new Lazy<IPatternChecker, PatternCheckerExportAttribute>[0];
            
        [Test]
        public void GetCheckerFor_Returns_Null_If_It_Has_No_Suitable_Checker()
        {
            var locator = new MEFPatternCheckersLocator(emptycheckersArray);
            Assert.IsNull(locator.GetCheckerFor(new Moq.Mock<IPattern>().Object));
        }

        [Test]
        public void GetCheckerFor_When_There_Is_Only_One_Checker_For_Given_Pattern()
        {
            var checker = new Moq.Mock<IPatternChecker>().Object;
            var exportMetadata = new PatternCheckerExportAttribute(typeof(DummyPattern));
            var locator = this.GetLocator(
                new[] { checker },
                new[] { exportMetadata });

            Assert.AreEqual(checker, locator.GetCheckerFor(new DummyPattern()));
        }

        [Test]
        public void GetCheckerFor_When_There_Are_Two_Checkers_For_Given_Pattern_And_One_OverridesDefault()
        {
            var defaultChecker = new Moq.Mock<IPatternChecker>().Object;
            var overrideChecker = new Moq.Mock<IPatternChecker>().Object;
            var defaultExportMetadata = new PatternCheckerExportAttribute(typeof(DummyPattern));
            var overrideExportMetadata = new PatternCheckerExportAttribute(typeof(DummyPattern))
                {
                    OverridesDefault = true
                };
            var locator = this.GetLocator(
                new[] { defaultChecker, overrideChecker }, 
                new[] { defaultExportMetadata, overrideExportMetadata });

            Assert.AreEqual(overrideChecker, locator.GetCheckerFor(new DummyPattern()));
        }

        [Test]
        [ExpectedException(typeof(MEFPatternCheckersLocator.CheckerLocationException))]
        public void GetCheckerFor_Throws_When_There_Are_Two_Checkers_For_Given_Pattern_And_None_OverridesDefault()
        {
            var defaultChecker = new Moq.Mock<IPatternChecker>().Object;
            var overrideChecker = new Moq.Mock<IPatternChecker>().Object;
            var exportMetadata = new PatternCheckerExportAttribute(typeof(DummyPattern));
            var locator = this.GetLocator(
                new[] { defaultChecker, overrideChecker },
                new[] { exportMetadata, exportMetadata });

            // this should throw
            locator.GetCheckerFor(new DummyPattern());
        }

        [Test]
        [ExpectedException(typeof(MEFPatternCheckersLocator.CheckerLocationException))]
        public void GetCheckerFor_Throws_When_There_Are_Three_Checkers_For_Given_Pattern()
        {
            var checker = new Moq.Mock<IPatternChecker>().Object;
            var exportMetadata = new PatternCheckerExportAttribute(typeof(DummyPattern));
            var locator = this.GetLocator(
                new[] { checker, checker, checker },
                new[] { exportMetadata, exportMetadata, exportMetadata });

            // this should throw
            locator.GetCheckerFor(new DummyPattern());
        }

        private MEFPatternCheckersLocator GetLocator(
            IPatternChecker[] checkers,
            PatternCheckerExportAttribute[] metadata)
        {
            Contract.Requires(checkers != null);
            Contract.Requires(metadata != null);
            Contract.Requires(checkers.Length == metadata.Length);

            var checkersAndMetadata = new List<Lazy<IPatternChecker, PatternCheckerExportAttribute>>();
            for (int i = 0; i < checkers.Length; i++)
            {
                int closureI = i;
                checkersAndMetadata.Add(new Lazy<IPatternChecker, PatternCheckerExportAttribute>(() => checkers[closureI], metadata[i]));
            }

            return new MEFPatternCheckersLocator(checkersAndMetadata.ToArray());
        }

        public class DummyPattern : PatternBase
        {            
        }
    }
}
